package me.kagami.gsp.service;


import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import me.kagami.gsp.util.PsiToolUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public abstract class GenerateDefaultForOneMethodTemplate {
    protected String generateName;
    protected Set<String> newImportList;
    protected StringBuilder builder;
    protected PsiMethod method;
    protected PsiLocalVariable localVariable;
    protected boolean simpleValue = false;

    protected static Map<String, String> typeGeneratedMap = new HashMap<String, String>() {
        {
            put("boolean", "false");
            put("java.lang.Boolean", "false");
            put("int", "0");
            put("byte", "(byte)0");
            put("java.lang.Byte", "(byte)0");
            put("java.lang.Integer", "0");
            put("java.lang.String", "\"\"");
            put("java.math.BigDecimal", "new BigDecimal(\"0\")");
            put("java.lang.Long", "0L");
            put("long", "0L");
            put("short", "(short)0");
            put("java.lang.Short", "(short)0");
            put("java.util.Date", "new Date()");
            put("float", "0.0F");
            put("java.lang.Float", "0.0F");
            put("double", "0.0D");
            put("java.lang.Double", "0.0D");
            put("java.lang.Character", "\'\'");
            put("char", "\'\'");
            put("java.time.LocalDateTime", "LocalDateTime.now()");
            put("java.time.LocalDate", "LocalDate.now()");

        }
    };

    protected static Map<String, String> typeGeneratedSimpleValueMap = new HashMap<String, String>() {
        {
            put("boolean", "true");
            put("java.lang.Boolean", "true");
            put("int", "1");
            put("byte", "(byte)1");
            put("java.lang.Byte", "(byte)1");
            put("java.lang.Integer", "1");
            put("java.lang.String", "\"Test\"");
            put("java.math.BigDecimal", "new BigDecimal(\"1\")");
            put("java.lang.Long", "1L");
            put("long", "1L");
            put("short", "(short)1");
            put("java.lang.Short", "(short)1");
            put("java.util.Date", "new Date()");
            put("float", "1.0F");
            put("java.lang.Float", "1.0F");
            put("double", "1.0D");
            put("java.lang.Double", "1.0D");
            put("java.lang.Character", "\'a\'");
            put("char", "\'a\'");
            put("java.time.LocalDateTime", "LocalDateTime.now()");
            put("java.time.LocalDate", "LocalDate.now()");

        }
    };


    protected static Map<String, String> typeGeneratedImport = new HashMap<String, String>() {
        {
            put("java.math.BigDecimal", "java.math.BigDecimal");
            put("java.util.Date", "java.util.Date");
            put("java.time.LocalDateTime", "java.time.LocalDateTime");
            put("java.time.LocalDate", "java.time.LocalDate");
        }
    };

    protected static Map<String, String> defaultCollections = new HashMap<String, String>() {
        {
            put("List", "ArrayList");
            put("Map", "HashMap");
            put("Set", "HashSet");
        }
    };

    protected static Map<String, String> defaultImportMap = new HashMap<String, String>() {
        {
            put("List", "java.util.ArrayList");
            put("Map", "java.util.HashMap");
            put("Set", "java.util.HashSet");
        }
    };

    protected static Map<String, String> defaultPackageValues = new HashMap<String, String>() {
        {
            put("java.sql.Date", "new Date(new java.util.Date().getTime())");
            put("java.sql.Timestamp", "new Timestamp(new java.util.Date().getTime())");
        }
    };

    public void generateDefaultForOneMethod() {
        PsiParameter[] parameters = method.getParameterList().getParameters();
        builder.append(generateName + "." + method.getName() + "(");
        int u = parameters.length;
        int h = 0;
        for (PsiParameter parameter : parameters) {
            h++;
            String classType = parameter.getType().getCanonicalText();
            //基本类型包括一些常见类型的处理
            String basicAndCommonType;
            if (isSimpleValue()) {
                basicAndCommonType = typeGeneratedSimpleValueMap.get(classType);
            } else {
                basicAndCommonType = typeGeneratedMap.get(classType);
            }
            if (basicAndCommonType != null) {
                builder.append(basicAndCommonType);
                String importType = typeGeneratedImport.get(classType);
                if (importType != null) {
                    newImportList.add(importType);
                }
            } else {
                Parameters paramInfo = PsiToolUtils.extractParamInfo(parameter.getType());
                //泛型类型的处理
                if (paramInfo.getCollectName() != null) {
                    String defaultImpl = defaultCollections.get(paramInfo.getCollectName());
                    if (defaultImpl != null) {
                        appendCollectNotEmpty(builder, paramInfo, defaultImpl, newImportList);
                        newImportList.add(defaultImportMap.get(paramInfo.getCollectName()));
                    } else {
                        appendCollectNotEmpty(builder, paramInfo, paramInfo.getCollectName(), newImportList);
                        newImportList.add(paramInfo.getCollectPackage());
                    }
                } else {
                    //其他类的处理,比如自己的类,Date之类的
                    String realPackage = paramInfo.getParams().get(0).getRealPackage();
                    String s = defaultPackageValues.get(realPackage);
                    final PsiClass psiClassOfParameter = PsiTypesUtil.getPsiClass(parameter.getType());
                    if (s != null) {
                        builder.append(s);
                    } else if (psiClassOfParameter != null && psiClassOfParameter.isEnum()) {
                        //枚举的处理
                        final PsiField[] allFields = psiClassOfParameter.getAllFields();
                        Arrays.stream(allFields).findFirst().ifPresent(field -> builder.append(psiClassOfParameter.getName()).append(".").append(field.getName()));
                    } else {
                        handle(parameter, paramInfo);
                    }
                }
            }
            if (h != u) {
                builder.append(",");
            }
        }
        builder.append(");");
    }

    protected static void appendCollectNotEmpty(StringBuilder builder, Parameters paramInfo, String defaultImpl, Set<String> newImportList) {
        builder.append("new " + defaultImpl + "<");
        for (int i = 0; i < paramInfo.getParams().size(); i++) {
            builder.append(paramInfo.getParams().get(i).getRealName());
            newImportList.add(paramInfo.getParams().get(i).getRealPackage());
            if (i != paramInfo.getParams().size() - 1) {
                builder.append(",");
            }
        }
        builder.append(">()");
    }

    protected abstract void handle(PsiParameter parameter, Parameters paramInfo);

    public String getGenerateName() {
        return generateName;
    }

    public void setGenerateName(String generateName) {
        this.generateName = generateName;
    }

    public Set<String> getNewImportList() {
        return newImportList;
    }

    public void setNewImportList(Set<String> newImportList) {
        this.newImportList = newImportList;
    }

    public StringBuilder getBuilder() {
        return builder;
    }

    public void setBuilder(StringBuilder builder) {
        this.builder = builder;
    }

    public PsiMethod getMethod() {
        return method;
    }

    public void setMethod(PsiMethod method) {
        this.method = method;
    }

    public PsiLocalVariable getLocalVariable() {
        return localVariable;
    }

    public void setLocalVariable(PsiLocalVariable localVariable) {
        this.localVariable = localVariable;
    }

    public boolean isSimpleValue() {
        return simpleValue;
    }

    public void setSimpleValue(boolean simpleValue) {
        this.simpleValue = simpleValue;
    }
}